package top.guoziyang.mydb.backend.dm.pageIndex;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import top.guoziyang.mydb.backend.dm.pageCache.PageCache;

/**
 * PageIndex类,用于管理页面休闲空间的索引，以便更有效地查找有足够空间的页面
 * 将页面划分为多个固定大小的空间区间，每个区间中保存页面的信息，以实现快速查找和插入
 */
public class PageIndex {
    private static final int INTERVALS_NO = 40; // 将一页划成40个区间
    private static final int THRESHOLD = PageCache.PAGE_SIZE / INTERVALS_NO; //每个区间的容量

    private Lock lock;
    private List<PageInfo>[] lists;

    @SuppressWarnings("unchecked")
    public PageIndex() {
        lock = new ReentrantLock();
        lists = new List[INTERVALS_NO+1];
        for (int i = 0; i < INTERVALS_NO+1; i ++) {
            lists[i] = new ArrayList<>();
        }
    }

    /**
     * add()方法的作用是计算页面剩余空间所对应的区间索引，封装页面的信息（封装在PageInfo对象中）并添加到lists[number]中
     * pgno: 当前页面的页面编号
     * freeSpace: 当前页面的空闲空间大小
     * number: 表示该页面的剩余空间所对应的区间索引； 例如，如果freeSpace为200，THRESHOLD为100，则number=2，表示该页面属于第二个空间区域
     */
    public void add(int pgno, int freeSpace) {
        lock.lock();
        try {
            // 计算区间索引
            int number = freeSpace / THRESHOLD;
            lists[number].add(new PageInfo(pgno, freeSpace));
        } finally {
            lock.unlock();
        }
    }

    /**
     * select方法的作用是寻找到一个合适的页面对象PageInfo，要求它的空闲空间大小>=spaceSize
     * spaceSize: 需要的空闲空间大小
     */
    public PageInfo select(int spaceSize) {
        lock.lock();
        try {
            // 计算number，表示spaceSize大致适合的区间编号
            int number = spaceSize / THRESHOLD;
            if(number < INTERVALS_NO){
                // +1的原因是：选择一个稍微大的区间，以保证有足够的freeSpace
                number ++;
            }
            //通过循环找到满足条件的PageInfo
            while(number <= INTERVALS_NO) {
                if(lists[number].size() == 0) {
                    // 如果该区间没有符合条件的页面,number++继续查找更大的区间
                    number ++;
                    continue;
                }
                // 如果找到非空的lists[number]，则删除并返回列表中的第一个PageInfo对象
                return lists[number].remove(0);
            }
            //如果所有区间都找到完毕，仍然没有符合条件的页面，则返回null
            return null;
        } finally {
            lock.unlock();
        }
    }

}
